// versions.go
package main

import (
	"Jam/config"
	"archive/zip"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

func ManageVersion() (string, error) {
	cfg, err := config.Load()
	if err != nil {
		return "", fmt.Errorf("配置加载失败: %v", err)
	}

	storageDir := cfg.Jar.StorageDir // 假设配置中存储路径为 "C:\\hea"

	// 确保目录存在
	if err := os.MkdirAll(storageDir, 0755); err != nil {
		return "", fmt.Errorf("创建目录失败: %v", err)
	}

	// 获取本地版本
	localVer, _ := getLocalVersion(storageDir)

	// 获取远程版本信息
	remoteVer, downloadURL, err := getRemoteVersion()
	if err != nil {
		return "", fmt.Errorf("获取远程版本失败: %v", err)
	}

	if compareVersions(localVer, remoteVer) < 0 {
		log.Printf("需要更新: 本地版本=%s 远程版本=%s", localVer, remoteVer)
		if downloadURL == "" {
			return "", fmt.Errorf("下载地址为空")
		}

		// 创建临时目录
		tempDir, err := os.MkdirTemp("", "hea-update")
		if err != nil {
			return "", fmt.Errorf("创建临时目录失败: %v", err)
		}
		defer os.RemoveAll(tempDir)

		// 下载更新包
		zipPath := filepath.Join(tempDir, "update.zip")
		if err := DownloadWithProgress(downloadURL, zipPath); err != nil {
			return "", fmt.Errorf("下载失败: %v", err)
		}

		// 解压更新包
		if err := extractZip(zipPath, storageDir); err != nil {
			return "", fmt.Errorf("解压失败: %v", err)
		}

		// 更新版本文件
		if err := os.WriteFile(filepath.Join(storageDir, "version.txt"),
			[]byte(remoteVer), 0644); err != nil {
			return "", fmt.Errorf("写入版本文件失败: %v", err)
		}

		log.Printf("更新完成，新版本: %s", remoteVer)
	} else {
		log.Printf("当前已是最新版本: %s", localVer)
	}

	// 查找解压后的JAR路径（根据实际需要调整）
	return findLatestJar(storageDir)
}

// 获取本地版本（从version.txt读取）
func getLocalVersion(storageDir string) (string, error) {
	versionFile := filepath.Join(storageDir, "version.txt")
	data, err := os.ReadFile(versionFile)
	if os.IsNotExist(err) {
		return "", nil
	}
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(data)), nil
}

// 获取远程版本信息
func getRemoteVersion() (string, string, error) {
	resp, err := http.Get("http://192.168.10.49:8080/api/version")
	if err != nil {
		return "", "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", "", fmt.Errorf("API返回状态码: %d", resp.StatusCode)
	}

	var response struct {
		Success bool `json:"success"`
		Data    struct {
			Version  string `json:"version"`
			Download string `json:"download"`
		} `json:"data"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return "", "", err
	}

	if !response.Success {
		return "", "", fmt.Errorf("API返回失败")
	}

	return response.Data.Version, response.Data.Download, nil
}

// 解压ZIP文件（自动处理顶层目录）
func extractZip(zipPath, targetDir string) error {
	// 清理旧的JAR文件
	if err := cleanupOldJars(targetDir); err != nil {
		log.Printf("警告: 清理旧JAR文件失败: %v", err)
	}
	r, err := zip.OpenReader(zipPath)
	if err != nil {
		return err
	}
	defer r.Close()

	// 检测公共根目录
	commonRoot := detectCommonRoot(r.File)
	hasCommonRoot := commonRoot != ""

	for _, f := range r.File {
		// 处理文件路径
		destPath := filepath.Join(targetDir, f.Name)
		if hasCommonRoot {
			// 移除公共根目录
			trimmedName := strings.TrimPrefix(f.Name, commonRoot)
			if trimmedName == "" { // 跳过根目录本身
				continue
			}
			destPath = filepath.Join(targetDir, trimmedName)
		}

		// 安全路径检查
		if !strings.HasPrefix(destPath, filepath.Clean(targetDir)+string(os.PathSeparator)) {
			return fmt.Errorf("非法文件路径: %s", f.Name)
		}

		// 创建目录或文件
		if f.FileInfo().IsDir() {
			os.MkdirAll(destPath, 0755)
			continue
		}

		// 确保父目录存在
		if err := os.MkdirAll(filepath.Dir(destPath), 0755); err != nil {
			return err
		}

		// 写入文件
		if err := writeFile(f, destPath); err != nil {
			return err
		}
	}
	return nil
}

// 检测ZIP包中的公共根目录
func detectCommonRoot(files []*zip.File) string {
	rootCounter := make(map[string]int)
	var mostCommonRoot string
	maxCount := 0

	for _, f := range files {
		// 分割路径为层级
		parts := strings.Split(f.Name, "/")
		if len(parts) == 0 {
			continue
		}

		// 获取有效根目录（忽略空字符串）
		var root string
		for _, p := range parts {
			if p != "" {
				root = p + "/"
				break
			}
		}

		// 统计根目录出现次数
		if root != "" {
			rootCounter[root]++
			if rootCounter[root] > maxCount {
				mostCommonRoot = root
				maxCount = rootCounter[root]
			}
		}
	}

	// 当超过80%的文件共享同一个根目录时视为公共根
	totalFiles := len(files)
	if totalFiles > 0 && float64(maxCount)/float64(totalFiles) >= 0.8 {
		return mostCommonRoot
	}
	return ""
}

// 安全写入文件
func writeFile(zipFile *zip.File, destPath string) error {
	// 如果是 .exe 且目标文件正在运行，写到临时文件
	isExe := strings.HasSuffix(strings.ToLower(destPath), ".exe")
	tmpPath := destPath
	if isExe {
		tmpPath = destPath + ".new"
	}

	// 打开源文件
	srcFile, err := zipFile.Open()
	if err != nil {
		return err
	}
	defer srcFile.Close()

	// 创建目标文件
	destFile, err := os.OpenFile(tmpPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, zipFile.Mode())
	if err != nil {
		return err
	}
	defer destFile.Close()

	_, err = io.Copy(destFile, srcFile)
	return err
}

// 版本比较函数（支持多段版本号）
func compareVersions(v1, v2 string) int {
	parse := func(v string) []int {
		var parts []int
		for _, s := range strings.Split(v, ".") {
			var n int
			fmt.Sscanf(s, "%d", &n)
			parts = append(parts, n)
		}
		return parts
	}

	v1Parts := parse(v1)
	v2Parts := parse(v2)

	maxLen := len(v1Parts)
	if len(v2Parts) > maxLen {
		maxLen = len(v2Parts)
	}

	for i := 0; i < maxLen; i++ {
		var a, b int
		if i < len(v1Parts) {
			a = v1Parts[i]
		}
		if i < len(v2Parts) {
			b = v2Parts[i]
		}

		if a < b {
			return -1
		}
		if a > b {
			return 1
		}
	}
	return 0
}

// 查找最新的JAR文件（根据实际情况调整）
func findLatestJar(storageDir string) (string, error) {
	// 示例：查找第一个.jar文件
	var jarPath string
	err := filepath.Walk(storageDir, func(path string, info os.FileInfo, err error) error {
		if err == nil && strings.HasSuffix(info.Name(), ".jar") {
			jarPath = path
			return io.EOF // 找到第一个即返回
		}
		return nil
	})

	if err == io.EOF {
		return jarPath, nil
	}
	return "", fmt.Errorf("未找到JAR文件")
}

// 清理旧的JAR文件
func cleanupOldJars(targetDir string) error {
	files, err := os.ReadDir(targetDir)
	if err != nil {
		return err
	}

	for _, file := range files {
		if strings.HasSuffix(strings.ToLower(file.Name()), ".jar") {
			filePath := filepath.Join(targetDir, file.Name())
			if err := os.Remove(filePath); err != nil {
				log.Printf("删除旧JAR文件失败 %s: %v", file.Name(), err)
			} else {
				log.Printf("已删除旧JAR文件: %s", file.Name())
			}
		}
	}
	return nil
}
